﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Scheme.Net.Runtime.AST;

namespace Scheme.Net.Runtime
{
    internal class Option<T> where T : class
    {
        private T value;

        protected Option(T value) { this.value = value; }

        public static Option<T> Some(T value) { return new Option<T>(value); }
        public static Option<T> None { get { return new Option<T>(null); } }

        public bool IsNone { get { return value == null; } }

        public T Value { get { return value; } }
    }

    internal static class EnumerableExtensions
    {
        public static T FoldRight<T, U>(this IEnumerable<U> list, Func<T, U, T> func, T acc)
        {
            var count = list.Count();
            for (int index = count - 1; index >= 0; index--)
                acc = func(acc, list.ElementAt(index));

            return acc;
        }

        public static IEnumerable<TResult> Unfold<T, TResult>(Func<T, Option<Tuple<TResult, T>>> generator, T start)
        {
            var next = start;

            while (true)
            {
                var res = generator(next);
                if (res.IsNone)
                    yield break;

                yield return res.Value.Item1;

                next = res.Value.Item2;
            }
        }
    }

    public static class Evaluable
    {
        public static bool IsNull(this IEvaluable x) { return x == Pair.Empty; }

        public static bool IsPair(this IEvaluable x)
        {
            if (x.IsNull()) return false;
            if (!(x is Pair)) return false;

            return true;
        }

        public static bool IsTaggedList(this IEvaluable x, string tag)
        {
            return x is Pair && (((Pair)x).Car == Symbol.Lookup(tag));
        }

        public static bool IsSelfEvaluating(this IEvaluable x)
        {
            return x is AST.Number || x is AST.String || x is Character || x.IsNull();
        }

        public static bool IsVariable(this IEvaluable x)
        {
            return x is Symbol;
        }

        public static bool IsFalse(this IEvaluable x)
        {
            if (x == Symbol.False)
                return true;

            return false;
        }

        public static bool IsTrue(this IEvaluable x)
        {
            return !IsFalse(x);
        }

        public static Pair MakeNullTerminated(this IEvaluable x)
        {
            if (x.IsNull()) return Pair.Empty;
            if (x.IsPair())
                return new Pair(((Pair)x).Car, ((Pair)x).Cdr.MakeNullTerminated());

            return new Pair(x, Pair.Empty);
        }
    }
}
